// <copyright file="YUIJavascriptSerializer.cs" company="YUIAsp.Net">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>raul popescu</author>
// <email>dev@yuiasp.net</email>
// <date>2009-01-30</date>
// <summary>YUIAsp.Net File</summary>

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;

namespace YUIAspNet.Common
{
    /// <summary>
    /// Custom YUI serializer, which does not render null, false property values.
    /// </summary>
    public class YUIJavascriptSerializer : JavaScriptSerializer
    {
        /// <summary>
        /// Serializes an complex object, removing the null properties.
        /// Also serializes dates as javascript Date object.
        /// </summary>
        /// <param name="value">The object to serialize.</param>
        /// <returns></returns>
        public StringBuilder YUISerialize(object value)
        {
            StringBuilder sbInternal = new StringBuilder();
            if (value == null || (value is bool && !((bool)value)))
                return null;

            if (value.GetType() == typeof(DateTime))
            {
                return sbInternal.Append(SerializeDate((DateTime)value));
            }
            if (value.GetType() == typeof(List<DateTime>))
            {
                foreach (DateTime date in (List<DateTime>)value)
                {
                    sbInternal.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", sbInternal.Length > 0 ? "," : "", SerializeDate(date));
                }
                sbInternal.Insert(0, "[");
                sbInternal.Append("]");

                return sbInternal;
            }
            Serialize(value, sbInternal);

            Regex regex = new Regex(",?\".[^\"]+?\":(null|false),?");

            Match match;
            do
            {
                match = regex.Match(sbInternal.ToString());
                if (match != null && match.Value.Length >0)
                {
                    if (match.Value.StartsWith(",", StringComparison.Ordinal) && match.Value.EndsWith(",", StringComparison.Ordinal))
                        sbInternal.Replace(match.Value.Substring(0, match.Value.Length - 1), ""); // keep the trailling ,
                    else
                        sbInternal.Replace(match.Value, "");
                }
            }
            while (match != null && match.Value.Length > 0);
           
            return sbInternal;
        }

        /// <summary>
        /// Serializes a date to javascript Date format.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns></returns>
        internal static string SerializeDate(DateTime date)
        {
            return string.Format(CultureInfo.InvariantCulture, "new Date({0},{1},{2},{3},{4})", date.Year, date.Month - 1, date.Day, date.Minute, date.Second);
        }

        /// <summary>
        /// Serializes a color in a hexadecimal number.
        /// </summary>
        /// <param name="color">The color.</param>
        /// <returns></returns>
        internal static string SerializeColor(Color color)
        {
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x2}{1:x2}{2:x2}", color.R, color.G, color.B);
        }

        /// <summary>
        /// Serializes a property.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        internal static string SerializeProperty(string key, object value)
        {
            if (value == null)
                return string.Format(CultureInfo.InvariantCulture, "{0}: null", key);

            Type actualType = value.GetType();

            if(actualType == typeof(int))
                return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", key, value);
            
            if(actualType == typeof(Color))
                return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", key, SerializeColor((Color)value));

            if (actualType == typeof(bool))
                return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", key, value.ToString().ToLowerInvariant());
            
            //default: serialize as string
            return string.Format(CultureInfo.InvariantCulture, "{0}: \"{1}\"", key, EscapeString(value.ToString()));
            
        }

        private static object EscapeString(string value)
        {
            return value.Replace("\"", "\\u0022").Replace("'", "\\u0027").Replace(Environment.NewLine, "\\n");
        }
    }
}
